﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using HeartLess.Managers;

namespace HeartLess.Core
{
    public class Hud
    {
        private Animation _heartBeat1;
        private Animation _heartBeat2;
        private SoundEffectInstance _heartBeat;
        private readonly static int FRAME_COUNT = 60;
        private readonly static int START_BEAT = 17;
        private readonly static int SOUND_DELAY = 250;  // millisecons
        private int _lastFrame = 0;
        private bool _startSound = false;

        private Texture2D _healthBG;
        private Single _health;
        private Color _healthColor = Color.Yellow;
        private Single _heartRate;
        private readonly int _healthHeight = 20;
        private readonly int _healthWidth = 200;

        public Hud()
        {
            _heartRate = 60;
            Texture2D animationTexture = AssetManager.GetAnimationTexture(AssetManager.ANIMATIONS.HEART_BEAT_1);
            _heartBeat1 = new Animation(animationTexture, FRAME_COUNT, FrameTime);
            animationTexture = AssetManager.GetAnimationTexture(AssetManager.ANIMATIONS.HEART_BEAT_2);
            _heartBeat2 = new Animation(animationTexture, FRAME_COUNT, FrameTime);

            SoundEffect sound = AssetManager.GetSound(AssetManager.SOUNDS.HEART_BEAT);
            _heartBeat = sound.CreateInstance();

            _healthBG = new Texture2D(HeartlessGame.Game.GraphicsDevice,
                                      1,
                                      1,
                                      false,
                                      SurfaceFormat.Color);
            Color[] color = { new Color(0.7f, 0.7f, 0.7f, 0.7f) };
            _healthBG.SetData(color);
        }

        public void Update(GameTime time)
        {
            _heartBeat1.FrameTimeLimit = FrameTime;
            _heartBeat2.FrameTimeLimit = FrameTime;
            _heartBeat1.Update(time);
            _heartBeat2.Update(time);

            Single colorRange = _heartRate;
            Color hrColor;
            if (_heartRate < 60.0f)
                hrColor = Interp(_heartRate, 40.0f, 60.0f, Color.Green, Color.Yellow);
            else
                hrColor = Interp(_heartRate, 60.0f, 120.0f, Color.Yellow, Color.Red);
            _heartBeat1.Tint = hrColor;
            _heartBeat2.Tint = hrColor;

            // calculate what frame we should start the sound
            int frame = CalcStartFrame();

            int lastFrame = _lastFrame;
            _lastFrame = _heartBeat1.CurrentFrameIndex;
            if (lastFrame > _lastFrame)
                lastFrame -= FRAME_COUNT;
            if (lastFrame < frame && frame <= _lastFrame)
                _startSound = true;

            _healthColor = Interp(_health, 0.0f, 240.0f, Color.Red, Color.Green);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            // Currently a very simple HUD: The health is displayed as text in an outlined box
            // The heartRate is displayed similarly

            Viewport vp = spriteBatch.GraphicsDevice.Viewport;
            int offY = vp.TitleSafeArea.Y;                  // Offset to the title safe area
            String hStr = ((int)Math.Ceiling(_health)).ToString();
            String hrStr = ((int)Math.Round(_heartRate)).ToString();

            spriteBatch.Draw(_healthBG, new Rectangle(vp.TitleSafeArea.X, offY, _healthWidth, _healthHeight), Color.White);
            Single width = _health;
            if (width < 0.0f)
                width = 0.0f;
            else if (width >= 240.0f)
                width = 240.0f;
            width = width * _healthWidth / 240.0f;
            spriteBatch.Draw(_healthBG, new Rectangle(vp.TitleSafeArea.X, offY + 1, (int)width, _healthHeight - 2), _healthColor);

            _heartBeat1.Location = new Vector2(Screen.CameraBounds.X + vp.TitleSafeArea.X, Screen.CameraBounds.Y + offY + _healthHeight + 2);
            _heartBeat2.Location = new Vector2(Screen.CameraBounds.X + vp.TitleSafeArea.X + LevelData.Level.UNIT_PIXEL_SIZE, Screen.CameraBounds.Y + offY + _healthHeight + 2);

            _heartBeat1.Draw(spriteBatch);
            _heartBeat2.Draw(spriteBatch);

            if (_startSound)
            {
                if (_heartBeat.State != SoundState.Stopped)
                    _heartBeat.Stop();
                else
                {
                    _heartBeat.Volume = .4f;
                    _heartBeat.Play();
                    _startSound = false;
                }
            }
        }

        public Single Health
        {
            get
            {
                return _health;
            }
            set
            {
                _health = value;
            }
        }

        public Single HeartRate
        {
            get
            {
                return _heartRate;
            }
            set
            {
                _heartRate = value;
            }
        }

        private int FrameTime
        {
            get
            {
                return Math.Max(1,(int)(60f * 1000.0f / (_heartRate * FRAME_COUNT)));
            }
        }

        private int CalcStartFrame()
        {
            int frame = (START_BEAT - SOUND_DELAY / FrameTime) % FRAME_COUNT;
            if (frame < 0)
                frame += FRAME_COUNT;
            return frame;
        }

        private Color Interp(Single v, Single v0, Single v1, Color c0, Color c1)
        {
            Single i = (v - v0) / (v1 - v0);
            if (i <= 0)
                return c0;
            if (i >= 1)
                return c1;
            Vector4 i0 = c0.ToVector4();
            Vector4 i1 = c1.ToVector4() - i0;
            Vector4 ret = i0 + i * i1;
            float f = Math.Max(ret.X, Math.Max(ret.Y, ret.Z));
            if (f <= 0.0f)
                f = 1.0f;
            return new Color(ret.X / f, ret.Y / f, ret.Z / f, ret.W);
        }
    }
}
